#include <cstring>
#include <cmath>
#include <iostream> //预处理器指令 a PREPROCESSOR directive
#include <vector>
#include <array>

using namespace std; // 使定义可见   make definitions visible

/*
  C++ primer 12.1
  一个不完善的String对象
  说明了复制构造函数  和 赋值运算符的作用 
  初始化和赋值的区别

*/

// StringBad是一个还没有开发好的示例。
// 完成了一些显而易见的工作，例如，它在构造函数和析构函数中正确地使用了new和delete。
class StringBad
{
private:
  char *str;              // pointer to string
  int len;                // length of string
  static int num_strings; // number of objects 记录所创建的对象数目
public:
  StringBad(const char *s); // constructor
  StringBad();              // default constructor
  ~StringBad();             // destructor

  StringBad(const StringBad& st); //复制构造函数 (深度复制)
  StringBad& operator=(const StringBad& st); //赋值运算符 (深度复制)

  // friend function
  friend std::ostream &operator<<(std::ostream &os,
                                  const StringBad &st);
};


void callme1(StringBad & rsb);
void callme2(StringBad sb);

int main()
{
  StringBad headline1("Celery");
  callme1(headline1);
  callme2(headline1); //值传递会调用析构函数

  //这样初始化对象不会用到赋值运算符 
  //使用复制构造函数创建一个临时对象，然后通过赋值将临时对象的值复制到新对象中。
  StringBad st1 = headline1; 

  //初始化和赋值的区别: 如果语句创建新的对象，则使用初始化；如果语句修改已有对象的值，则是赋值

  //下面这种赋值会使用赋值运算符,没有重载 = 时, 成员复制有问题，即导致headlinel.str和knot.str指向相同的地址,因此，当对knot调用析构函数后,还会对headlinel.str调用析构函数,删除str的内存
  //试图删除已经删除的数据导致的结果是不确定的，因此可能改变内存中的内容，导致程序异常终止。

  StringBad knot; //声明时调用默认构造函数
  knot = headline1; //覆盖knot原来的地址,析构函数会先把knot的内存释放,导致出错

  //结果num_strings 为-1 调用callme2()时多调用了一次析构函数

  //这两种写法是等效的,都不会调用参数为const char*的构造函数,但程序结束的时候会调用析构函数
  //当您使用一个对象来初始化另一个对象时,编译器会默认生成 StringBad(const StringBad &);
  StringBad sailor = headline1;
  StringBad sailor2 = StringBad(headline1);//constructor using sports




}


void callme1(StringBad & rsb)
{   
    //String passed by reference 
    cout << "通过引用传递的字符串：";
    cout << "    \"" << rsb << "\"\n";
}

//将headline2作为函数参数来传递从而导致析构函数被调用。
void callme2(StringBad sb)
{ 
    //String passed by value
    cout << "通过值传递的字符串: ";
    cout << "    \"" << sb << "\"\n";
}

//复制构造函数 (深度复制)
//每当程序生成了对象副本时，编译器都将使用复制构造函数。
StringBad::StringBad(const StringBad& st){
  num_strings++;//handle static member update
  len = st.len;//same length
  str=new char[len+1];//allot space
  std::strcpy(str,st.str);//copy string to new location
  cout<< num_strings <<": \""<<str  <<"\" object copy created \n";//For Your Information
}

//赋值运算符 (深度复制)
StringBad& StringBad::operator=(const StringBad& st){
  //函数应当避免将对象赋给自身
  if(this == &st)//object assigned to itself 
    return *this;//all done

  //由于目标对象可能引用了以前分配的数据,释放这些数据
  delete[] str;//free old string
  len = st.len;
  str = new char[len+1];//get space for new string
  std::strcpy(str,st.str);//copythestring
  return *this;//return reference to invoking object 返回调用对象的引用 
}


// initializing static class member
int StringBad::num_strings = 0;

// class methods

// construct StringBad from C string
StringBad::StringBad(const char *s)
{
  len = std::strlen(s);    // set size
  str = new char[len + 1]; // allot storage 分配存储
  std::strcpy(str, s);     // initialize pointer 初始化指针 
  num_strings++;           // set object count 

  // For Your Information 供参考
  cout << num_strings << ": \"" << str << "\" object created\n"; 
}

StringBad::StringBad() // default constructor
{
  len = 4;
  str = new char[4];
  std::strcpy(str, "C++"); // default string
  num_strings++;

  // FYI
  cout << num_strings << ": \"" << str << "\" default object created\n";
}

//析构函数
StringBad::~StringBad() // necessary destructor
{
  cout << "\"" << str << "\" object deleted, "; // FYI
  --num_strings;                                // required
  cout << num_strings << " left\n";             // FYI
  delete[] str;                                 // required 这里注释防止程序报错,因为StringBad类是个不完善
                                                // 增加 赋值运算符 (深度复制) 复制构造函数 (深度复制) 后就不需要注释了
}

//友元函数 重载 << , 使cout可以输出StringBad对象
std::ostream &operator<<(std::ostream &os, const StringBad &st)
{
  os << st.str;
  return os;
}
